package cool.mtc.minghe.web.init;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import cool.mtc.minghe.data.annotation.DictEnum;
import cool.mtc.minghe.data.entity.sys.dict.DictDO;
import cool.mtc.minghe.data.entity.sys.dict.DictTypeDO;
import cool.mtc.minghe.data.entity.sys.org.OrgDO;
import cool.mtc.minghe.data.entity.sys.permission.PermissionDO;
import cool.mtc.minghe.data.entity.sys.role.RoleDO;
import cool.mtc.minghe.data.entity.sys.role.RolePermissionDO;
import cool.mtc.minghe.data.entity.sys.user.UserAuthDO;
import cool.mtc.minghe.data.entity.sys.user.UserDO;
import cool.mtc.minghe.data.entity.sys.user.UserOrgRoleDO;
import cool.mtc.minghe.data.enums.DictEnumSupport;
import cool.mtc.minghe.data.enums.OrgTypeEnum;
import cool.mtc.minghe.data.enums.UploadTypeEnum;
import cool.mtc.minghe.data.enums.UserStatusEnum;
import cool.mtc.minghe.service.mapper.sys.org.OrgMapper;
import cool.mtc.minghe.service.mapper.sys.permission.PermissionMapper;
import cool.mtc.minghe.service.mapper.sys.role.RoleMapper;
import cool.mtc.minghe.service.mapper.sys.role.RolePermissionMapper;
import cool.mtc.minghe.service.mapper.sys.user.UserAuthMapper;
import cool.mtc.minghe.service.mapper.sys.user.UserMapper;
import cool.mtc.minghe.service.service.sys.dict.DictService;
import cool.mtc.minghe.service.service.sys.dict.DictTypeService;
import cool.mtc.minghe.service.service.sys.user.UserOrgRoleService;
import cool.mtc.security.constant.AuthTypeConstant;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * @author 明河
 * <p>
 * 服务启动后初始化数据
 */
@Slf4j
@Component
@RequiredArgsConstructor(onConstructor = @__(@Autowired))
public class Init implements ApplicationRunner {
    /**
     * 预置数据的编码前缀
     */
    private static final String PRESET_SUFFIX = "$$_";

    private final DictTypeService dictTypeService;
    private final DictService dictService;
    private final UserOrgRoleService userOrgRoleService;

    private final UserMapper userMapper;
    private final UserAuthMapper userAuthMapper;
    private final OrgMapper orgMapper;
    private final RoleMapper roleMapper;
    private final PermissionMapper permissionMapper;
    private final RolePermissionMapper rolePermissionMapper;

    @Override
    public void run(ApplicationArguments args) {
        // 用户状态字典
        this.initDict(UserStatusEnum.class, UserStatusEnum.values());

        // 单位类型
        this.initDict(OrgTypeEnum.class, OrgTypeEnum.values());

        // 上传类型字典
        this.initDict(UploadTypeEnum.class, UploadTypeEnum.values());

        // 系统管理员
        this.initSysAdmin();

        log.info("初始化完成");
    }

    /**
     * 初始化系统管理员用户
     */
    private void initSysAdmin() {
        log.info("系统管理员账号初始化开始");
        Long userId = this.initSysAdminUser();
        Long orgId = this.initSysAdminOrg();
        Long roleId = this.initSysAdminRoleAndPermission();

        UserOrgRoleDO userOrgRoleDO = new UserOrgRoleDO();
        userOrgRoleDO.setUserId(userId);
        userOrgRoleDO.setOrgId(orgId);
        userOrgRoleDO.setRoleId(roleId);
        userOrgRoleDO.setPreset(true);
        userOrgRoleService.update(userId, Collections.singletonList(userOrgRoleDO));
        log.info("系统管理员账号初始化完成");
    }

    /**
     * 初始化系统管理员的账号数据
     *
     * @return userId
     */
    private Long initSysAdminUser() {
        // 用户
        UserDO userDO = new UserDO();
        userDO.setNickName("系统管理员");
        userDO.setStatusDictId(this.getDictId(UserStatusEnum.ENABLE));
        userDO.setPreset(true);
        QueryWrapper<UserDO> userWrapper = new QueryWrapper<>();
        userWrapper.eq("nick_name", userDO.getNickName());
        userWrapper.eq("is_preset", 1);
        UserDO existUserDO = userMapper.selectOne(userWrapper);
        Long userId;
        if (null == existUserDO) {
            userMapper.insert(userDO);
            userId = userDO.getId();
        } else {
            userId = existUserDO.getId();
        }

        // 用户认证；不存在则新增，存在不更新，以防密码被修改
        UserAuthDO userAuthDO = new UserAuthDO();
        userAuthDO.setUserId(userId);
        userAuthDO.setType(AuthTypeConstant.PASSWORD);
        userAuthDO.setIdentifier("sys");
        userAuthDO.setCredential(new BCryptPasswordEncoder().encode("sys@123"));

        QueryWrapper<UserAuthDO> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id", userAuthDO.getUserId());
        wrapper.eq("identifier", userAuthDO.getIdentifier());
        wrapper.eq("type", userAuthDO.getType());
        long count = userAuthMapper.selectCount(wrapper);
        if (0 == count) {
            userAuthMapper.insert(userAuthDO);
        }

        return userId;
    }

    /**
     * 初始化系统管理员的单位数据
     *
     * @return orgId
     */
    private Long initSysAdminOrg() {
        OrgDO orgDO = new OrgDO();
        orgDO.setName("系统管理员");
        orgDO.setLongName(orgDO.getName());
        orgDO.setTypeDictId(this.getDictId(OrgTypeEnum.SYS_ADMIN));
        orgDO.setOrderIndex(1);
        orgDO.setPreset(true);

        QueryWrapper<OrgDO> wrapper = new QueryWrapper<>();
        wrapper.eq("name", orgDO.getName());
        wrapper.eq("type_dict_id", orgDO.getTypeDictId());
        OrgDO existOrgDO = orgMapper.selectOne(wrapper);
        if (null == existOrgDO) {
            orgMapper.insert(orgDO);
            return orgDO.getId();
        }
        return existOrgDO.getId();
    }

    /**
     * 初始化系统管理员的角色和权限数据
     *
     * @return roleId
     */
    private Long initSysAdminRoleAndPermission() {
        // 角色
        RoleDO roleDO = new RoleDO();
        roleDO.setCode(PRESET_SUFFIX + "SYS_ADMIN");
        roleDO.setName("系统管理员");
        roleDO.setOrderIndex(1);
        roleDO.setPreset(true);
        roleDO.setDescription("系统预设数据");

        QueryWrapper<RoleDO> roleWrapper = new QueryWrapper<>();
        roleWrapper.eq("code", roleDO.getCode());
        RoleDO existRoleDO = roleMapper.selectOne(roleWrapper);

        Long roleId;
        if (null == existRoleDO) {
            roleMapper.insert(roleDO);
            roleId = roleDO.getId();
        } else {
            roleId = existRoleDO.getId();
        }

        // 权限
        PermissionDO permissionDO = new PermissionDO();
        permissionDO.setName("系统所有权限");
        permissionDO.setPermission("*:*:*");
        permissionDO.setOrderIndex(1);
        permissionDO.setPreset(true);
        permissionDO.setDescription("系统预设数据");

        QueryWrapper<PermissionDO> permissionWrapper = new QueryWrapper<>();
        permissionWrapper.eq("permission", permissionDO.getPermission());
        PermissionDO existPermissionDO = permissionMapper.selectOne(permissionWrapper);

        Long permissionId;
        if (null == existPermissionDO) {
            permissionMapper.insert(permissionDO);
            permissionId = permissionDO.getId();
        } else {
            permissionId = existPermissionDO.getId();
        }

        // 角色权限关联表
        RolePermissionDO rolePermissionDO = new RolePermissionDO();
        rolePermissionDO.setRoleId(roleId);
        rolePermissionDO.setPermissionId(permissionId);
        rolePermissionDO.setPreset(true);

        QueryWrapper<RolePermissionDO> rolePermissionWrapper = new QueryWrapper<>();
        rolePermissionWrapper.eq("role_id", rolePermissionDO.getRoleId());
        rolePermissionWrapper.eq("permission_id", rolePermissionDO.getPermissionId());
        long count = rolePermissionMapper.selectCount(rolePermissionWrapper);
        if (0 == count) {
            rolePermissionMapper.insert(rolePermissionDO);
        }

        return roleId;
    }

    /**
     * 初始化字典数据，包括字典类型及字典数据
     */
    private <T extends DictEnumSupport> void initDict(Class<T> clazz, T[] enums) {
        log.info("字典{}初始化开始", clazz.getName());

        // 字典类型
        Long dictTypeId = this.initDictType(clazz);
        if (null == dictTypeId) {
            return;
        }

        // 字典数据
        this.initDictData(dictTypeId, enums);

        log.info("字典{}初始化完成", clazz.getName());
    }

    /**
     * 初始化字典类型
     */
    private <T extends DictEnumSupport> Long initDictType(Class<T> clazz) {
        DictTypeDO dictTypeDO = new DictTypeDO();
        DictEnum dictEnum = clazz.getAnnotation(DictEnum.class);
        if (null == dictEnum) {
            log.error("字典{}初始化失败；失败原因：未添加[{}]注解", clazz.getName(), DictEnum.class.getName());
            return null;
        }
        dictTypeDO.setCode(this.handlePresetCode(dictEnum.dictTypeCode()));
        dictTypeDO.setName(dictEnum.dictTypeName());
        dictTypeDO.setDescription("系统预设数据");
        dictTypeDO.setPreset(true);
        return dictTypeService.saveWithUpdate(dictTypeDO);
    }

    /**
     * 初始化字典数据
     */
    private void initDictData(Long dictTypeId, DictEnumSupport[] enums) {
        if (null == dictTypeId) {
            return;
        }
        List<DictDO> dictList = new ArrayList<>(enums.length);
        for (DictEnumSupport item : enums) {
            DictDO dictDO = item.transToDictDO(dictTypeId);
            dictDO.setCode(this.handlePresetCode(dictDO.getCode()));
            dictDO.setDescription("系统预设数据");
            dictList.add(dictDO);
        }
        dictService.saveWithUpdate(dictList);
    }

    /**
     * 从枚举中获取对应的字典ID
     */
    private <T extends DictEnumSupport> Long getDictId(T t) {
        DictEnum dictEnum = t.getClass().getAnnotation(DictEnum.class);
        return dictService.getIdByDictTypeCodeAndCode(this.handlePresetCode(dictEnum.dictTypeCode()), this.handlePresetCode(t.name()));
    }

    private String handlePresetCode(String code) {
        return code.startsWith(PRESET_SUFFIX) ? code : (PRESET_SUFFIX + code);
    }
}
